package grex.Nodes;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */
import grex.BNFException;
import grex.ErrorManagement.ErrorManager;
import grex.Options;
import grex.Data.Prediction;
import grex.Data.PredictionContainer;
import grex.EvolutionProcess;
import grex.IPredictiveModel;

import java.text.NumberFormat;
import java.util.Arrays;

public class EnsPred extends Node implements ITerminal {

    private static final int NROFCILDREN = 0;
    private double value = -1;
    private Double prob = new Double(0);
    private Double lowerProb = new Double(-1);
    private int count = 0;
    public double[] support = null;
    private int majority = -1, totalSupport = 0;
    private double[] probs = null;
    private Node predictionCopy=null;
    private double regInternalValue=0;
    private PredictionContainer trainInstances;
    private String fold=null;
    public EnsPred() throws BNFException {
        super(NROFCILDREN);
        
    }
    private IPredictiveModel ensemble=null;
    public synchronized void execute(Node parent, PredictionContainer data, int mode) throws GeneException {
        this.parent = parent;
        if ((mode == Node.TRAIN_MODE || mode == Node.OPTIMIZE_MODE) && !modified){
              return;
        }else{
           modified = false;
        }//*/
       
        if (mode == Node.TRAIN_MODE || mode == Node.OPTIMIZE_MODE) {
            if(fold==null)
                fold = environment.getOptions().getTestFOLD();
            trainInstances = data;
            prob = new Double(0);
            lowerProb = new Double(0);
            count = data.values().size();
                           
            
            predictionCopy = (Node) clone();
            
            predictionCopy.parent = null;
            ensemble = EvolutionProcess.extractionData.getModel(fold);
        }
        ensemble.execute(data);
        for (Prediction p : data.values()) {
        //    p.setProbs(getProbs());           
           p.setLeaf(predictionCopy);
        }//*/
    }


    public void setValue(double value) {
        this.value = value;
    }

    public double getValue() {
        return value;
    }

    public void setProb(double prob) {
        this.prob = prob;
    }

    @Override
    public synchronized double getProb() {
        if(count == 0)
            return 0;
        
        if(majority!=-1){
            if(probs == null)
                probs = getProbs();
            return getProbs()[majority];        
        }else
            return 0;
    }

    public synchronized Object clone() {
        EnsPred newEnsPred;
        try {
            newEnsPred = new EnsPred();
            newEnsPred.ensemble = ensemble;
            newEnsPred.ops=ops;
            newEnsPred.environment=environment;
            newEnsPred.value = value;
            newEnsPred.prob =new Double(prob);
            newEnsPred.lowerProb = new Double(lowerProb);
            newEnsPred.count = count;
            newEnsPred.level = level;
            if (support != null) {
                newEnsPred.support = Arrays.copyOf(support, support.length);
            }
            if(probs !=null)
                newEnsPred.probs = Arrays.copyOf(probs, probs.length);
            newEnsPred.majority = majority;
            newEnsPred.totalSupport = totalSupport;
            newEnsPred.modified = modified;
            newEnsPred.predictionCopy = predictionCopy;
            newEnsPred.regInternalValue = regInternalValue;
        } catch (grex.BNFException e) {
            ErrorManager.getInstance().reportError(e);
            return null;
        }
        return newEnsPred;
    }

   /* public double getPrediction(Prediction p) {        
        init();
        if (super.ops.getPROBLEM_TYPE() == Options.CLASSIFICATION) {
            return majority;
        } else {
            return value;
        }
    }*/

    /** @todo Om endast enn kategori finns kommer denna att volja lika ofta somma alla andra sammtidigt */
    public synchronized String toString() {
        return "EnsPred";
    }

    private void init() {
        if (support != null && majority == -1) {
            totalSupport = 0;
            majority = 0;
            double maxSupport = 0;
            for (int i = 0; i < support.length; i++) {
                totalSupport += support[i];
                if (support[i] > maxSupport) {
                    maxSupport = support[i];
                    majority = i;
                }
            }
            
        }
    }

    public synchronized double[] getProbs() {
        if (probs == null && support != null) {
            
            probs = new double[support.length];
            if(support.length==1){//Regression
                probs[0]=value;
                
                return probs;
            }                        
            init();
            if (totalSupport > 0) {
                for (int i = 0; i < support.length; i++) {                                        
                    if(environment.getOptions().isLAPLACE())
                        probs[i] = (1.0 + support[i]) / (support.length + totalSupport);
                    else
                        probs[i] = (support[i]) / (totalSupport);
                }
            }
            
            
             
            
            
            
        }
        if (probs == null) {
            return new double[1];//only happens if an intron happens to exists in a tree that is displayed.
        }
        return probs;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public double getLowProb() {
        return lowerProb;
    }

    public boolean equals(Object o) {
        if (o instanceof EnsPred) {
                return true;
            }
        return false;
    }

    /**
     * @return the regInternalValue
     */
    public double getRegInternalValue() {
        return regInternalValue;
    }

    /**
     * @param regInternalValue the regInternalValue to set
     */
    public void setRegInternalValue(double regInternalValue) {
        this.regInternalValue = regInternalValue;
    }
}

